# !/usr/bin/env python3
# coding=utf8
"""
CTA回测的配置相关
"""
import dataclasses
import datetime
import json
import logging
import math
import pathlib
from typing import Any, Dict, List, Set, Tuple, Type, Optional, Union, Callable
import vnpy.tools.utility
import vnpy.trader.utility
import vnpy_ctastrategy.engine
from vnpy.trader.constant import Exchange, Interval
from vnpy_ctastrategy.backtesting import BacktestingEngine, BacktestingMode
from vnpy_ctastrategy.template import CtaTemplate


def strategy_name_2_strategy_class(strategy_name: str) -> Type[CtaTemplate]:
    """"""
    cta_engine: vnpy_ctastrategy.engine.CtaEngine = vnpy_ctastrategy.engine.CtaEngine(main_engine=None, event_engine=None)
    cta_engine.load_strategy_class()
    strategy_class: Type[CtaTemplate] = cta_engine.classes[strategy_name]
    return strategy_class


@dataclasses.dataclass
class CtaConfig:
    """CtaConfig"""
    vt_symbol: str = ""  # 本地代码
    interval: Interval = Interval.MINUTE  # K线周期
    start: datetime.datetime = None  # 开始日期
    end: datetime.datetime = None  # 结束日期
    rate: float = 0  # 手续费率, "费率(0.001)表示成交额的千分之一(0.1%)"
    slippage: float = 0  # 交易滑点, "损耗=交易滑点*成交量*合约乘数,滑点是对成交价的偏移"
    size: float = 1  # 合约乘数, "成交额=成交价*成交量*合约乘数"
    pricetick: float = 0  # 价格跳动
    capital: int = 1_000_000  # 回测资金
    mode: BacktestingMode = BacktestingMode.BAR
    risk_free: float = 0
    annual_days: int = 240
    half_life: int = 120
    strategy_class: Type[CtaTemplate] = None  # 交易策略
    parameters: Dict[str, dict] = None  # 参数范围
    target_name: str = None  # 优化目标
    backtest_resume: int = 0  # 回测中断续测用途
    abandon_results: bool = False  # 丢弃回测结果
    do_continue: bool = False  # 使用续跑模式


@dataclasses.dataclass
class CtaConfigLite:
    """精简版"""
    vt_symbol: str = ""  # 本地代码
    interval: Interval = None  # K线周期
    start: datetime.datetime = None  # 开始日期
    end: datetime.datetime = None  # 结束日期
    strategy_class: Type[CtaTemplate] = None  # 交易策略
    parameters: Dict[str, dict] = None  # 参数范围
    target_name: str = None  # 优化目标
    backtest_resume: int = 0  # 回测中断续测用途
    abandon_results: bool = False  # 丢弃回测结果
    do_continue: bool = False  # 使用续跑模式

    def to_cta_config(self) -> CtaConfig:
        """"""
        symbol, _ = vnpy.trader.utility.extract_vt_symbol(self.vt_symbol)
        product: str = vnpy.tools.utility.get_product(symbol=symbol)

        tdx_mapping: Dict[str, dict] = vnpy.tools.utility.load_tdx_code2name_ini(filepath=None)
        product_info: dict = tdx_mapping[product]

        commission_data = vnpy.tools.utility.load_ctp_commission()
        commission_item = commission_data.get(product)

        if commission_item:
            fixed4slippage = math.ceil(commission_item["fixed"] / product_info["multiple"] * (10**6)) / (10**6)
            assert math.isclose(commission_item["fixed"], fixed4slippage * product_info["multiple"]) or commission_item["fixed"] < fixed4slippage * product_info["multiple"]
            slippage_pricetick: int = 0
            slippage = product_info["price_tick"] * slippage_pricetick + fixed4slippage
            rate = commission_item["ratio"]
            logging.debug(f'使用载入值, vt_symbol={self.vt_symbol}, slippage={slippage}, rate={rate},')
        else:
            slippage_pricetick: int = 1
            slippage = product_info["price_tick"] * slippage_pricetick
            rate = 0.0
            logging.debug(f'使用默认值, vt_symbol={self.vt_symbol}, slippage={slippage}, rate={rate},')

        cta_config: CtaConfig = CtaConfig(
            vt_symbol=self.vt_symbol,
            interval=self.interval,
            start=self.start,
            end=self.end,
            rate=rate,
            slippage=slippage,
            size=product_info["multiple"],
            pricetick=product_info["price_tick"],
            capital=1_000_000,
            mode=BacktestingMode.BAR,
            risk_free=0,
            annual_days=240,
            half_life=120,
            strategy_class=self.strategy_class,
            parameters=self.parameters,
            target_name=self.target_name,
            backtest_resume=self.backtest_resume,
            abandon_results=self.abandon_results,
            do_continue=self.do_continue,
        )

        return cta_config

    @classmethod
    def load_from_file(cls, filepath: Union[str, pathlib.Path]) -> "CtaConfigLite":
        """"""
        with open(file=filepath, mode='r', encoding='utf8') as f:
            json_obj: dict = json.load(fp=f)

        vt_symbol: str = json_obj["vt_symbol"]
        interval: Interval = Interval(json_obj["interval"])
        start: datetime.datetime = datetime.datetime.strptime(json_obj["start"], "%Y-%m-%d")
        end: datetime.datetime = datetime.datetime.strptime(json_obj["end"], "%Y-%m-%d")

        strategy_name: str = json_obj["strategy_name"]
        strategy_class: Type[CtaTemplate] = strategy_name_2_strategy_class(strategy_name=strategy_name)

        parameters: dict = json_obj["parameters"]
        target_name: str = json_obj["target_name"]

        backtest_resume: int = json_obj["backtest_resume"]
        assert isinstance(backtest_resume, int)

        abandon_results: bool = json_obj["abandon_results"]
        assert isinstance(abandon_results, bool)

        do_continue: bool = json_obj["do_continue"]

        cta_config_lite: CtaConfigLite = CtaConfigLite(
            vt_symbol=vt_symbol,
            interval=interval,
            start=start,
            end=end,
            strategy_class=strategy_class,
            parameters=parameters,
            target_name=target_name,
            backtest_resume=backtest_resume,
            abandon_results=abandon_results,
            do_continue=do_continue,
        )
        return cta_config_lite


if __name__ == "__main__":
    pass
